package com.nvwa.coding.model;

import com.nvwa.coding.function.FunctionEX;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;


/**
 * 一种表示“不是这个，就是那个”的双返回类型
 * <p>但是不会同时返回两个值</p>
 * <p>一般用于方法的返回值，对外表示有两种互斥的可能结果，但只会返回其中一种结果</p>
 * <p>内部的值无法被修改，调用修改方法会产生新的副本</p>
 * <p>通过{@link #otherwise()}交换主要和次要的位置，调用{@code otherwise()}后，接下来的操作都将针对原先的次要类型</p>
 * <hr>
 * <pre>
 * {@code
 *       Either<BigDecimal, String> result1 = calculate1();
 *       StringBuilder msg = result1.filter(Coding::isMoreThanZero)    // 只要大于0的值
 *                                  .ifPresent(System.out::println)    // 如果存在，打印
 *                                  .otherwise()                       // 交换主要次要，接下来对次要值进行操作
 *                                  .ifPresent(System.out::println)    // 如果次要值存在，打印
 *                                  .map(StringBuilder::new)           // 如果次要值存在，转换成StringBuilder
 *                                  .orNull();                         // 取出次要值，如果没有，返回null
 * }
 * </pre>
 * <hr>
 *
 * @param <MAIN>  主要返回类型，默认对该对象的操作都基于此值
 * @param <OTHER> 次要返回类型，用于被交换，不会被直接操作
 */
public class Either<MAIN, OTHER> extends Opt<MAIN> {

    protected final OTHER other;

    protected final boolean isMain;

    protected Either(MAIN main, OTHER other, boolean isMain) {
        super(main);
        this.other = other;
        this.isMain = isMain;
    }

    public static <MAIN, OTHER> Either<MAIN, OTHER> main(MAIN main) {
        return new Either<>(main, null, true);
    }

    public static <MAIN, OTHER> Either<MAIN, OTHER> other(OTHER other) {
        return new Either<>(null, other, false);
    }

    /**
     * <h2>当前是否是main（即使main是null，也返回true）</h2>
     * <hr>
     * <pre>
     * {@code
     *      Either<String, Integer> result1 = Either.main(null);
     *      result1.isMain() // true;
     *      result1.isPresent(); // false
     *      result1.orNull() // null;
     *      result1.otherwise().isMain() // false;
     *      Either<String, Integer> result2 = Either.other(1);
     *      result2.isMain() // false;
     *      result2.orNull() // null;
     *      result2.otherwise().isMain() // true;
     *      result2.otherwise().orNull() // 1;
     * }
     * </pre>
     * <hr>
     */
    public boolean isMain() {
        return isMain;
    }

    /**
     * 否则的话xxx
     * <p>将主要和次要的位置互换</p>
     * <hr>
     * <pre>
     * {@code
     *     Either<String, Integer> result1 = Either.main("demo");
     *     result1.orNull(); // "demo"
     *     Either<Integer, String> result1_otherwise = result1.otherwise();
     *     result1_otherwise.orNull() // null
     *
     *     Either<String, Integer> result2 = Either.other(1);
     *     result2.orNull(); // null
     *     Either<Integer, String> result2_otherwise = result2.otherwise();
     *     result2_otherwise.orNull() // 1
     * }
     * </pre>
     * <hr>
     */
    public Either<OTHER, MAIN> otherwise() {
        return new Either<>(other, value, !isMain);
    }

    /**
     * 否则的话xxx
     * <p>将主要和次要的位置互换</p>
     * <hr>
     * <pre>
     * {@code
     *     Either<String, Integer> result1 = Either.main("demo");
     *     result1.orNull(); // "demo"
     *     Either<Integer, String> result1_otherwise = result1.otherwise();
     *     result1_otherwise.orNull() // null
     *
     *     Either<String, Integer> result2 = Either.other(1);
     *     result2.orNull(); // null
     *     Either<Integer, String> result2_otherwise = result2.otherwise();
     *     result2_otherwise.orNull() // 1
     * }
     * </pre>
     * <hr>
     * @see #otherwise()
     */
    public Either<OTHER, MAIN> either() {
        return otherwise();
    }

    /**
     * 否则的话xxx
     * <p>将主要和次要的位置互换</p>
     * <hr>
     * <pre>
     * {@code
     *     Either<String, Integer> result1 = Either.main("demo");
     *     result1.orNull(); // "demo"
     *     Either<Integer, String> result1_otherwise = result1.otherwise();
     *     result1_otherwise.orNull() // null
     *
     *     Either<String, Integer> result2 = Either.other(1);
     *     result2.orNull(); // null
     *     Either<Integer, String> result2_otherwise = result2.otherwise();
     *     result2_otherwise.orNull() // 1
     * }
     * </pre>
     * <hr>
     * @see #otherwise()
     */
    public Either<OTHER, MAIN> reverse() {
        return otherwise();
    }

    /**
     * 否则的话xxx
     * <p>将主要和次要的位置互换</p>
     * <hr>
     * <pre>
     * {@code
     *     Either<String, Integer> result1 = Either.main("demo");
     *     result1.orNull(); // "demo"
     *     Either<Integer, String> result1_otherwise = result1.otherwise();
     *     result1_otherwise.orNull() // null
     *
     *     Either<String, Integer> result2 = Either.other(1);
     *     result2.orNull(); // null
     *     Either<Integer, String> result2_otherwise = result2.otherwise();
     *     result2_otherwise.orNull() // 1
     * }
     * </pre>
     * <hr>
     * @see #otherwise()
     */
    public Either<OTHER, MAIN> other() {
        return otherwise();
    }

    /**
     * 保留other，返回一个更换了main的Either
     */
    public <U> Either<U, OTHER> withMain(U main) {
        return new Either<>(main, other, true);
    }

    /**
     * @see #main(Object)
     * @deprecated 不可用
     */
    @Deprecated
    public static <T> Opt<T> check(T value) {
        throw new UnsupportedOperationException("Dont call #check in Either!");
    }

    /**
     * @see #main(Object)
     * @deprecated 不可用
     */
    @Deprecated
    public static <T> Opt<T> of(T value) {
        throw new UnsupportedOperationException("Dont call #of in Either!");
    }

    /**
     * @see #main(Object)
     * @deprecated 不可用
     */
    @Deprecated
    public static <T> Opt<T> of(@SuppressWarnings("OptionalUsedAsFieldOrParameterType") Optional<T> optional) {
        throw new UnsupportedOperationException("Dont call #of in Either!");
    }

    public boolean is(MAIN value) {
        return Objects.equals(this.value, value);
    }

    public MAIN value() {
        return value;
    }


    /**
     * <h2>如果当前either是主要值，进行操作</h2>
     * <h3>注意，操作方法需处理null值，如果不想要null值，请使用{@link #ifPresent(Consumer)}</h3>
     * <hr>
     * <pre>
     * {@code
     *     Either<String, Integer> result1 = Either.main(null); // 返回一个主要值，但值为null
     *     result1.ifPresent(System.out::println);  // 因为值为null，所以不执行
     *     result1.ifMain(System.out::println);     // 因为当前either是main，所以可以执行
     *     result1.ifOther(System.out::println);    // 因为当前either是main而不是other，所以不执行
     *
     *     Either<String, Integer> result2 = Either.other(1);   // 返回一个次要值
     *     result2.ifPresent(System.out::println);  // 使用主要值，但either不是main，主要值视为null，所以不执行
     *     result2.ifMain(System.out::println);     // either不是main，所以不执行
     *     result2.ifOther(System.out::println);    // either是other，所以可以执行
     *     // 交换主次
     *     result2.otherwise().ifPresent(System.out::println);  // 使用原次要值，因为次要值为1，所以可以执行
     *     result2.otherwise().ifMain(System.out::println);     // 因为当前either完成交换后是main，所以可以执行
     *     result2.otherwise().ifOther(System.out::println);    // 因为当前either完成交换后是main，所以不执行
     * }
     * </pre>
     * <hr>
     *
     * @param consumer 操作方法
     * @return this
     * @see #ifOther(Consumer)
     */
    public Either<MAIN, OTHER> ifMain(Consumer<? super MAIN> consumer) {
        if (isMain()) {
            consumer.accept(value);
        }
        return this;
    }

    /**
     * <h2>如果当前either是次要值，进行操作</h2>
     * <h3>注意，操作方法需处理null值，如果不想要null值，请使用{@link #ifPresent(Consumer)}</h3>
     * <h3>和{@link #ifMain(Consumer)}的执行条件相反</h3>
     *
     * @param consumer 操作方法
     * @return this
     * @see #ifMain(Consumer)
     */
    public Either<MAIN, OTHER> ifOther(Consumer<? super MAIN> consumer) {
        if (!isMain()) {
            consumer.accept(value);
        }
        return this;
    }

    @Override
    public Either<MAIN, OTHER> ifPresent(Consumer<? super MAIN> consumer) {
        return (Either<MAIN, OTHER>) super.ifPresent(consumer);
    }

    @Override
    public Either<MAIN, OTHER> peek(Consumer<? super MAIN> consumer) {
        return (Either<MAIN, OTHER>) super.peek(consumer);
    }

    @Override
    public Either<MAIN, OTHER> ifAbsent(Runnable runnable) {
        return (Either<MAIN, OTHER>) super.ifAbsent(runnable);
    }

    @Override
    public Either<MAIN, OTHER> filter(Predicate<? super MAIN> predicate) {
        return (Either<MAIN, OTHER>) super.filter(predicate);
    }

    @Override
    public <R> Either<MAIN, OTHER> filterField(Function<MAIN, R> getFieldFun, Predicate<? super R> predicate) {
        return (Either<MAIN, OTHER>) super.filterField(getFieldFun, predicate);
    }

    @Override
    public Either<MAIN, OTHER> drop(Predicate<? super MAIN> predicate) {
        return (Either<MAIN, OTHER>) super.drop(predicate);
    }

    @Override
    public <R> Either<MAIN, OTHER> dropField(Function<MAIN, R> getFieldFun, Predicate<? super R> predicate) {
        return (Either<MAIN, OTHER>) super.dropField(getFieldFun, predicate);
    }

    @Override
    public <U> Either<U, OTHER> map(Function<? super MAIN, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent()) {
            return withMain(null);
        } else {
            return withMain(mapper.apply(value));
        }
    }

    @Override
    public <U, EX extends Throwable> Either<U, OTHER> safeMap(FunctionEX<? super MAIN, ? extends U, EX> mapper) {
        return map(mapper.toLoggingFunction(null));
    }

    @Override
    public <U, EX extends Throwable> Either<U, OTHER> loggingMap(FunctionEX<? super MAIN, ? extends U, EX> mapper) {
        return map(mapper.toLoggingFunction(null));
    }

    @Override
    public <U, EX extends Throwable> Either<U, OTHER> silentMap(FunctionEX<? super MAIN, ? extends U, EX> mapper) {
        return map(mapper.toLoggingFunction(null));
    }

    @Override
    public <U> Either<U, OTHER> mapOpt(Function<? super MAIN, Opt<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent()) {
            return withMain(null);
        } else {
            return withMain(Objects.requireNonNull(mapper.apply(value)).value);
        }
    }

    @Override
    public <U> Either<U, OTHER> mapOptional(Function<? super MAIN, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent()) {
            return withMain(null);
        } else {
            return withMain(Objects.requireNonNull(mapper.apply(value)).orElse(null));
        }
    }

    @Override
    public Either<MAIN, OTHER> orWrap(MAIN orWrap) {
        return isPresent() ? this : withMain(orWrap);
    }

    @Override
    public Either<MAIN, OTHER> orWrap(Supplier<? extends MAIN> orWrap) {
        return isPresent() ? this : withMain(orWrap.get());
    }

    @Override
    public Either<MAIN, OTHER> orUse(Opt<MAIN> orUse) {
        return isPresent() ? this : withMain(orUse.value);
    }

    @Override
    public Either<MAIN, OTHER> orUse(Supplier<? extends Opt<MAIN>> orUse) {
        return isPresent() ? this : withMain(orUse.get().value);
    }

    @Override
    public <X extends Throwable> Either<MAIN, OTHER> orThrow(Supplier<? extends X> exceptionSupplier) throws X {
        return (Either<MAIN, OTHER>) super.orThrow(exceptionSupplier);
    }

    @Override
    public String toString() {
        return String.format("Either[%s, %s]", value, other);
    }

}
